package com.enjoyread.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.enjoyread.common.bo.UserBo;
import com.enjoyread.common.bo.UserCodeBo;
import com.enjoyread.common.config.RedisKeyConfig;
import com.enjoyread.common.config.SystemConfig;
import com.enjoyread.common.utils.BeanUtil;
import com.enjoyread.common.utils.EncryptUtil;
import com.enjoyread.common.utils.RUtils;
import com.enjoyread.common.utils.StringUtils;
import com.enjoyread.common.vo.R;
import com.enjoyread.core.dao.UserDao;
import com.enjoyread.core.dao.UserLogDao;
import com.enjoyread.core.entity.User;
import com.enjoyread.core.entity.UserLog;
import com.enjoyread.core.service.intf.UserService;
import com.enjoyread.core.utils.JedisUtil;
import com.enjoyread.core.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private UserLogDao userLogDao;
    //将配置文件里的私钥注入到String prikey中
    @Value("${cs.pass.prikey}")
    private String prikey;

    @Override
    @Transactional//开启事务
    public R register(UserBo bo, String ip) {
        // 1.校验bo是否为空
        if (bo != null) {
            //2.校验手机号和密码的格式
            if (StringUtils.isNotEmpty(bo.getPassword()) && StringUtils.regexPhone(bo.getPhone())) {
                //3.校验,是否校验过验证码
                if (JedisUtil.checkSet(RedisKeyConfig.REGISTER_PHONES, bo.getPhone())) {
                    //4.校验该手机号是否已经被注册
                    if (userDao.selectOne(new QueryWrapper<User>().eq("phone", bo.getPhone())) == null) {
                        //5.组装注册的对象，把bo的属性赋值给user
                        User user = BeanUtil.copyProperty(User.class, bo, bo.getClass().getDeclaredFields());
                        //6.注册，记录这个用户的状态
                        user.setFlag(SystemConfig.USER_FLAG_OK);
                        //7.密码的密文处理
                        user.setPassword(EncryptUtil.rsaEnc(prikey, user.getPassword()));
                        //8.将数据分别添加到t_user和t_userlog表中
                        if (userDao.insert(user) > 0) {
                            userLogDao.insert(new UserLog(user.getId(), SystemConfig.USER_OP_ADD, "注册新用户，密码注册", ip, new Date()));
                            return RUtils.ok(null);
                        }
                        return RUtils.ok("注册成功！", null);
                    }
                    return RUtils.fail("此手机号已被注册");
                }
                return RUtils.fail("未通过验证码验证");
            }
            return RUtils.fail("账号或密码格式有误，请检查");
        }
        return RUtils.fail("错误！");
    }

    //验证手机号是否可以注册（是否注册了）
    @Override
    public R checkPhone(String phone) {
        //1.入参校验
        if (StringUtils.regexPhone(phone)) {
            //2.查询数据库，看手机号是否已经被注册
            User user = userDao.selectOne(new QueryWrapper<User>().eq("phone", phone));
            //3.校验
            if (user == null) {
                return RUtils.ok("该手机号未注册", null);
            } else {
                return RUtils.fail("改手机号已注册！");
            }
        }
        return RUtils.fail("手机号格式有误");
    }

    //手机号密码登录
    @Override
    public R loginPassword(UserBo bo, String ip) {
        //1.校验bo是否为空
        if (bo != null) {
            //2.校验手机号和密码
            if (StringUtils.isNotEmpty(bo.getPassword()) && StringUtils.regexPhone(bo.getPhone())) {
                //3.查询数据库是否已经注册
                User user = userDao.selectOne(new QueryWrapper<User>().eq("phone", bo.getPhone()));
                //4.校验user不为空
                if (user != null) {
                    //5.校验user的状态
                    if (user.getFlag() == SystemConfig.USER_FLAG_OK) {
                        //6.校验密码
                        if (user.getPassword().equals(EncryptUtil.rsaEnc(prikey, bo.getPassword()))) {
                            //7.校验当前账号是否已经在线，如果在线，不允许登录，唯一登录
                            //此key后面的值是token，若数据库中不存在，说明此账号不是登陆状态，可以登录，并生成token
                            if (!JedisUtil.checkKey(RedisKeyConfig.LOGIN_PHONE + bo.getPhone())) {
                                //8.生成令牌
                                String token = JwtUtils.createToken(user.getId() + "");
                                //9.Redis储存 性能+有效期 唯一登录 String类型
                                //记录当前令牌--用户信息 把用户信息和令牌绑定在一起，以通过令牌获取用户信息
                                JedisUtil.addStr(RedisKeyConfig.LOGIN_TOKEN + token, JSON.toJSONString(user), RedisKeyConfig.LOGIN_TOKEN_TIME);
                                //记录当前手机号--令牌 唯一登录  token只有一个，当一个token没有失效之前，不能获取另一个，唯一登录
                                JedisUtil.addStr(RedisKeyConfig.LOGIN_PHONE + bo.getPhone(), token, RedisKeyConfig.LOGIN_TOKEN_TIME);
                                //10.记录日志
                                userLogDao.insert(new UserLog(user.getId(), SystemConfig.USER_OP_LOGIN, "手机号密码登录成功", ip, new Date()));
                                return RUtils.ok("登陆成功！", token);

                            } else {
                                return RUtils.fail("该账号已在线，请勿重复登陆");
                            }
                        }
                        return RUtils.fail("账号或密码有误");
                    }
                    return RUtils.fail("账号状态异常，无法登录，请联系客服");
                }
                return RUtils.fail("该账号未注册");
            }
            return RUtils.fail("请输入正确的手机号");
        }
        return RUtils.fail("网络异常");
    }

    @Override
    @Transactional
    public R loginCode(UserCodeBo bo, String ip) {
        //校验bo不为空
        if (bo != null) {
            //校验手机号
            if (StringUtils.regexPhone(bo.getPhone())) {
                //校是否发送了验证码
                String key = RedisKeyConfig.SMS_LCODE + bo.getPhone();
                if (JedisUtil.checkKey(key)) {
                    //校验输入的验证码
                    if (bo.getCode() == Integer.parseInt(JedisUtil.getStr(key))) {
                        //校验是否在线
                        if (!JedisUtil.checkKey(RedisKeyConfig.LOGIN_PHONE + bo.getPhone())) {
                            //校验user信息是否已经注册，若没有注册，则自动注册
                            User user = userDao.selectOne(new QueryWrapper<User>().eq("phone", bo.getPhone()));
                            //空则自动注册
                            if (user == null) {
                                user = new User();
                                //设置默认密码
                                user.setPassword(EncryptUtil.rsaEnc(prikey, bo.getCode() + ""));
                                user.setFlag(SystemConfig.USER_FLAG_OK);
                                user.setPhone(bo.getPhone());
                                //将用户的信息存到t_user表中
                                userDao.insert(user);
                                //记录日志
                                userLogDao.insert(new UserLog(user.getId(), SystemConfig.USER_OP_ADD, "手机号验证码登陆时，自动注册", ip, new Date()));
                            }
                            //生成令牌
                            String token = JwtUtils.createToken(user.getId() + "");
                            //Redis储存 性能+有效期 唯一登录 String类型
                            //记录当前令牌--用户信息 把用户信息和令牌绑定在一起，以通过令牌获取用户信息
                            JedisUtil.addStr(RedisKeyConfig.LOGIN_TOKEN + token, JSON.toJSONString(user), RedisKeyConfig.LOGIN_TOKEN_TIME);
                            //记录当前手机号--令牌 唯一登录  token只有一个，当一个token没有失效之前，不能获取另一个，唯一登录
                            JedisUtil.addStr(RedisKeyConfig.LOGIN_PHONE + user.getPhone(), token, RedisKeyConfig.LOGIN_TOKEN_TIME);
                            //记录日志
                            userLogDao.insert(new UserLog(user.getId(), SystemConfig.USER_OP_LOGIN, "手机验证码登陆成功", ip, new Date()));
                            //销毁验证码
                            JedisUtil.delKey(key);
                            return RUtils.ok("ok", token);
                        } else {
                            return RUtils.fail("用户已在线");
                        }
                    }
                    return RUtils.fail("验证码输入有误");
                }
                return RUtils.fail("未发送验证码");
            }
            return RUtils.fail("请输入正确的手机号");
        }
        return RUtils.fail("验证码错误！");
    }

    @Override
    public R findPass(UserCodeBo bo, String ip) {
        //入参校验
        if (bo != null) {
            //校验手机号是否已注册（没注册怎么找回密码）
            if (userDao.selectOne(new QueryWrapper<User>().eq("phone", bo.getPhone())) != null) {
                if (StringUtils.regexPhone(bo.getPhone())) {
                    //验证是否已发送验证码
                    String key = RedisKeyConfig.SMS_FCODE + bo.getPhone();
                    if (JedisUtil.checkKey(key)) {
                        //校验输入的验证码
                        if (bo.getCode() == Integer.parseInt(JedisUtil.getStr(key))) {
                            //验证通过，清理验证码
                            JedisUtil.delKey(key);
                            //同时记录通过验证的手机号,用set 因为唯一
                            JedisUtil.addSet(RedisKeyConfig.FIND_PHONES, bo.getPhone());
                            return RUtils.ok("验证通过，请设置密码", "null");
                        }
                        return RUtils.fail("验证码错误");
                    }
                    return RUtils.fail("未发送验证码");
                }
                return RUtils.fail("手机号格式不正确");
            }
            return RUtils.fail("账号未注册");
        }
        return RUtils.fail("发生了意外错误！");
    }

    @Override
    @Transactional
    public R updatePass(UserBo bo, String ip) {
        //入参校验
        if (bo != null) {
            //校验手机号格式
            if (StringUtils.regexPhone(bo.getPhone())) {
                //校验验证码是否已经通过找回密码的验证（查手机号）
                String key = RedisKeyConfig.FIND_PHONES;
                if (JedisUtil.checkSet(key, bo.getPhone())) {
                    //通过验证 修改密码
                    userDao.updatePass(EncryptUtil.rsaEnc(prikey, bo.getPassword()), bo.getPhone());
                    //记录日志 找回密码成功
                    userLogDao.insert(new UserLog(-1, SystemConfig.USER_OP_FIND, "手机号找回密码成功", ip, new Date()));
                    //删除校验验证码的记录（防止一个验证码多次找回密码）
                    JedisUtil.delSet(key, bo.getPhone());
                    //如果账号在线，需要退出登录，删除token
                    String keyPhone = RedisKeyConfig.LOGIN_PHONE + bo.getPhone();
                    if (JedisUtil.checkKey(keyPhone)) {
                        //通过keyPhone可以获得token，通过RedisKeyConfig.LOGIN_TOKEN+token删除
                        JedisUtil.delKey(RedisKeyConfig.LOGIN_TOKEN + JedisUtil.getStr(keyPhone));
                        //在将自己删除
                        JedisUtil.delKey(keyPhone);
                    }
                    return RUtils.ok("操作成功", null);
                }
                return RUtils.fail("手机号未通过验证码验证");
            }
            return RUtils.fail("手机号格式不正确");
        }
        return RUtils.fail("未知错误");
    }

    @Override
    public R checkToken(String token) {
        //入参校验
        if (token != null) {
            //校验token
            if (JedisUtil.checkKey(RedisKeyConfig.LOGIN_TOKEN + token)) {
                return RUtils.ok("有效", null);
            }
            return RUtils.fail("无效");
        }
        return null;
    }

    @Override
    public R loginOut(String token) {
        //入参校验
        if (token != null) {
            //校验是否在线
            String key = RedisKeyConfig.LOGIN_TOKEN + token;
            if (JedisUtil.checkKey(key)) {
                //清理token
                /**
                 * @author: JC
                 * @time: 2021/10/19 14:43
                 * 通过RedisKeyConfig.LOGIN_TOKEN+token这个key可以查出附带的user信息，
                 * 把user信息用JSON.parseObject()转换为user对象，然后通过user对象获得user的手机号，删除
                 * RedisKeyConfig.LOGIN_PHONE+user.getPhone()这个key的值，值是token，再把RedisKeyConfig.LOGIN_TOKEN+token
                 * 这个key删除，这样两个就都删了
                 *
                 */
                User user = JSON.parseObject(JedisUtil.getStr(key), User.class);
                JedisUtil.delKey(RedisKeyConfig.LOGIN_PHONE + user.getPhone());
                JedisUtil.delKey(RedisKeyConfig.LOGIN_TOKEN + token);
                return RUtils.ok("注销成功", null);
            }
            return RUtils.ok("令牌已失效，已经自动注销", null);
        }
        return RUtils.fail("注销失败");
    }

    @Override
    public R queryUserInfo(String token) {

        //1.入参校验
        if (StringUtils.isNotEmpty(token)) {
            //2.校验令牌
            String key = RedisKeyConfig.LOGIN_TOKEN + token;
            if (JedisUtil.checkKey(key)) {
                //3.获取令牌对应的用户信息
                User user = JSON.parseObject(JedisUtil.getStr(key), User.class);
                user.setPassword("");//数据脱敏
                //4.返回结果
                return RUtils.ok(user);
            }

            //入参校验
            if (token != null) {
                //校验是否在线

                if (JedisUtil.checkKey(key)) {
                    //获得用户数据
                    User user = JSON.parseObject(JedisUtil.getStr(key), User.class);
                    //数据脱敏
                    user.setPassword("");
                    return RUtils.ok(user);
                }
                return RUtils.fail("令牌已失效");

            }
            return RUtils.fail("失败");
        }
        return RUtils.fail("失败");
    }
}
